package com.sl.common.util;/*
 * Copyright [2022] [https://www.xiaonuo.vip]
 *
 * Snowy采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：
 *
 * 1.请不要删除和修改根目录下的LICENSE文件。
 * 2.请不要删除和修改Snowy源码头部的版权声明。
 * 3.本项目代码可免费商业使用，商业使用请保留源码和相关描述文件的项目出处，作者声明等。
 * 4.分发源码时候，请注明软件出处 https://www.xiaonuo.vip
 * 5.不可二次分发开源参与同类竞品，如有想法可联系团队xiaonuobase@qq.com商议合作。
 * 6.若您的项目无法满足以上几点，需要更多功能代码，获取Snowy商业授权许可，请在官网购买授权，地址为 https://www.xiaonuo.vip
 */

import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SM4;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

import java.nio.charset.StandardCharsets;
//import org.bouncycastle.crypto.engines.SM2Engine;
//import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

/**
 * 加密工具类，本框架目前使用 https://github.com/antherd/sm-crypto 项目中一些加解密方式
 * 使用小伙伴需要过等保密评相关，请在此处更改为自己的加密方法，或加密机，使用加密机同时需要替换公钥，私钥在内部无法导出，提供加密的方法
 * 如果不涉及到加密机方面的内容，请更改公私要为自己重新生成的，生成方式请看集成的sm-crypto主页
 *
 * @author yubaoshan
 * @date 2022/9/15 21:51
 */
@Slf4j
public class CommonCryptogramUtil {

    /**
     * 公钥
     */
    private static final String PUBLIC_KEY = "04232f5d18af7a80519cf7ee64088136eb925a5cb6b1f71673ccf1b9ce0bc4aed5686ba693f8dd2aa9110bb1787c5820173d8dff9b0bc7fff3e72dbc57bc15c10f";

    /**
     * 私钥
     */
    private static final String PRIVATE_KEY = "308193020100301306072a8648ce3d020106082a811ccf5501822d0479307702010104203391226dba15d5daafc0d17d25c8cdf6e4e0e29f3a89a91f2398acd29783ef24a00a06082a811ccf5501822da14403420004232f5d18af7a80519cf7ee64088136eb925a5cb6b1f71673ccf1b9ce0bc4aed5686ba693f8dd2aa9110bb1787c5820173d8dff9b0bc7fff3e72dbc57bc15c10f";


    /**
     * SM4的对称秘钥（生产环境需要改成自己使用的） 16 进制字符串，要求为 128 比特
     */
    private static final String KEY = "0123456789abcdeffedcba9876543210";

    /**
     * 解密方法
     * 如果采用加密机的方法，用try catch 捕捉异常，返回原文值即可
     *
     * @param str 密文
     * @return 解密后的明文
     */
    public static String doSm2Decrypt(String str) {
        // 解密
        return SmUtil.sm2(PRIVATE_KEY, PUBLIC_KEY).decryptStr("04" + str, KeyType.PrivateKey);
    }
    public static String doSm2Encrypt(String str) {
        // 解密
        return SmUtil.sm2(PRIVATE_KEY, PUBLIC_KEY).encryptHex("04" + str, KeyType.PrivateKey);
    }

    public static void main(String[] args) {

        String s = doSm4CbcEncrypt("123456");
        System.out.println(s);
        String s1 = doSm4CbcDecrypt(s);
        System.out.println(s1);

//创建sm2 对象
        SM2 sm = SmUtil.sm2();

        // sm2的加解密时有两种方式即 C1C2C3、 C1C3C2，
        sm.setMode(SM2Engine.Mode.C1C3C2);

        // 生成私钥
        String privateKey = HexUtil.encodeHexStr(sm.getPrivateKey().getEncoded());
        log.info("私钥: {}", privateKey);

        // 生成公钥
        String publicKey = HexUtil.encodeHexStr(sm.getPublicKey().getEncoded());
        log.info("公钥: {}", publicKey);

        // 生成私钥 D，以D值做为js端的解密私钥
        String privateKeyD = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm.getPrivateKey()));
        log.info("私钥D: {}", privateKeyD);

        // 生成公钥 Q，以Q值做为js端的加密公钥
        String publicKeyQ = HexUtil.encodeHexStr(((BCECPublicKey) sm.getPublicKey()).getQ().getEncoded(false));
        log.info("公钥Q: {}", publicKeyQ);
    }

    /**
     * 加密方法
     *
     * @param str 待加密数据
     * @return 加密后的密文
     */
    public static String doSm4CbcEncrypt(String str) {
        // SM4 加密  cbc模式
//        Sm4Options sm4Options4 = new Sm4Options();
//        sm4Options4.setMode("cbc");
//        sm4Options4.setIv("fedcba98765432100123456789abcdef");
        SM4 sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, HexUtil.decodeHex(KEY),
                HexUtil.decodeHex("fedcba98765432100123456789abcdef"));
        return sm4.encryptHex(str);
    }

    /**
     * 解密方法
     * 如果采用加密机的方法，用try catch 捕捉异常，返回原文值即可
     *
     * @param str 密文
     * @return 解密后的明文
     * @author yubaoshan
     * @date 2022/9/15 21:51
     */
    public static String doSm4CbcDecrypt(String str) {
        // 解密，cbc 模式，输出 utf8 字符串
//        Sm4Options sm4Options8 = new Sm4Options();
//        sm4Options8.setMode("cbc");
//        sm4Options8.setIv("fedcba98765432100123456789abcdef");
        SM4 sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, HexUtil.decodeHex(KEY),
                HexUtil.decodeHex("fedcba98765432100123456789abcdef"));

        String docString = null;
        try {
            docString = sm4.decryptStr(str);
        } catch (Exception e) {

        }

        if ("".equals(docString)) {
            log.warn(">>> 字段解密失败，返回原文值：{}", str);
            return str;
        } else {
            return docString;
        }
    }


    /**
     * 纯签名
     *
     * @param str 待签名数据
     * @return 签名结果
     */
    public static String doSignature(String str) {
        SM2 sm2 = SmUtil.sm2(PRIVATE_KEY, PUBLIC_KEY);
        byte[] sign = SmUtil.sm2(PRIVATE_KEY, PUBLIC_KEY).sign(str.getBytes(StandardCharsets.UTF_8));
        return new String(sign);
        // return Sm2.doSignature(str, PRIVATE_KEY);
    }


    /**
     * 通过杂凑算法取得hash值，用于做数据完整性保护
     *
     * @param str 字符串
     * @return hash 值
     * @author yubaoshan
     * @date 2022/9/15 21:51
     */
    public static String doHashValue(String str) {
        return SmUtil.sm3(str);
    }
}
